#include <iostream>
#include <string>
#include <string.h>

using std::cout;
using std::endl;
using std::string;

//总结：
//1、模板参数列表就是类型列表
//2、模板参数列表的参数个数 = 
//  整个函数形态(包括返回类型，比如
//  template <class _Container>
//  back_inserter_iterator<_Container> back_inserter_iterator(_Container & _X))
//  类体里有用多少种 不确定的类型
//比如3个int，2个long，则两个参数
//
//3、如果没有在<>中填写类型，则会根据函数参数的类型，反向推导出T1、T2
//add(T1 x, T2 y)
//add(1, 2.2)
//add<int,double>(1, 2.2)
//但是对于类，必须在<>里写出类型

template<typename T>    // <>是模板参数列表，typename == class
T add(T x,T y){     
    cout<<"T add(T,T)"<<endl;
    return x+y;
}

//函数模板与函数模板之间也形成重载关系
template<typename T>
T add(T x,T y,T z){
    cout<<"T add(T,T,T)"<<endl;
    return x+y+z;
}

//函数模板与函数之间也会形成重载，函数优先于函数模板
int add(int x,int y){
    cout<<"int add(int,int)"<<endl;
    return x+y;
}

//模板的特化：有些类型推导出来编译不过，这时可以将其参数列表单独写出
//分类：
//全特化：参数全部给出
//偏特化：不全部给出
template<>
//const char *add<const char *>(const char *px, const char *py){
const char *add(const char *px, const char *py){
    cout<<"自己写c风格字符串add"<<endl;
    char *ptmp=new char[strlen(px)+strlen(py)+1]();
    strcpy(ptmp,px);
    strcat(ptmp,py);

    return ptmp;
}

void test(){
    int ia=3,ib=4;
    double da=3.3,db=4.4;
    char ca='a',cb=1;
    string s1("hello");
    string s2("world");

    //可以显式、隐式实例化
    cout<<"add(ia,ib)="<<add<int>(ia,ib)<<endl;
    cout<<"add(da,db)="<<add(da,db)<<endl;
    cout<<"add(ca,cb)="<<add(ca,cb)<<endl;
    cout<<"add(s1,s2)="<<add(s1,s2)<<endl;

    cout<<endl;
    const char *pstr1="hello";
    const char *pstr2="world";
    cout<<add(pstr1,pstr2)<<endl; //模板推导没问题，但是c风格字符串不能相加

}

int main()
{   
    test();
    return 0;
}

